
package org.nViron.nvGraffiti;

import instar.dg.Caching.AbstractCache;
import instar.dg.Caching.CacheListener;
import instar.dg.Common.ErrorListener;
import instar.dg.Common.Serializable;
import instar.dg.Debug.Debug;
import instar.dg.util.TaskQueue;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class nvIOCacheSE implements AbstractCache {

    public final static nvIOCacheSE instance = new nvIOCacheSE();
    
    protected TaskQueue taskIOQueue = new TaskQueue();

	public void checkDirectory(String path) throws Exception {
        try {
            File fconn = new File( path );
            // If no exception is thrown, then the URI is valid, but the file may or may not exist.
            if ( !fconn.exists() ) {
                fconn.mkdir();
                fconn.setReadable(true);
                fconn.setWritable(true);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new Exception( "checkDir failed because of: " + ex.toString() );
        }
    }

	public void delete(String filename) {
        File file = null;
        try {
            file = new File(filename);
            if (file.exists()) {
                file.delete();
            }
        } catch (Throwable ex) {
            Debug.out.println("PersistentCacheME throws " + ex.toString());
            ex.printStackTrace();
        }
    }

	public boolean exists(String filename) {
        boolean ret = false;
        File fconn = new File(filename );
        // If no exception is thrown, then the URI is valid, but the file may or may not exist.
        ret = fconn.exists();
        return ret;
    }

    /**
     * Loads the given {@link Serializable}-object from the given filename.
     *
     * @return the loaded object
     */
	public Serializable load(String filename, Serializable obj) throws Throwable {
        File fconn = new File(filename);
        if (fconn.exists() && (fconn.length() > 0)) {
            FileInputStream is = new FileInputStream(fconn);
            DataInputStream dis = new DataInputStream(is);
            obj.readFromDataInput(dis);
            dis.close();
            is.close();
            dis = null;
        }
        return obj;
    }

    /**
     * Requests the loading of the file given with <c>filename</c>. The data of the file is loaded by the given {@link Serializable}-object. If the file has been loaded
     * or an error occured, the related method of the {@link CacheListener} will be called.
     */
	public void loadAsync(final String filename, final Serializable obj, final CacheListener listener) {
        taskIOQueue.addTask(new Runnable() {

			public void run() {
                try {
                    Serializable result = load(filename, obj);
                    if (listener != null) {
                        listener.objectLoaded(filename, result);
                    }
                } catch (Throwable ex) {
                    if (listener != null) {
                        listener.onError(new instar.dg.Common.Error(0, ex.toString()));
                    }
                    ex.printStackTrace();
                }
            }
        });
    }

    public void queueTask( Runnable task ) {
        taskIOQueue.addTask(task);
    }

    /**
     * Saves an {@link Serializable} object in the current Cache object with the given filename.
     */
	public void save(String filename, Serializable obj) throws Throwable {
        File fconn = new File(filename);
        // If no exception is thrown, then the URI is valid, but the file may or may not exist.
        if (fconn.exists()) {
            fconn.delete();
            //#debug
//            Debug.out.println(System.currentTimeMillis() + " ms - [" + Thread.currentThread().getName() + "] PersistentCacheMe.addHeader('" + filename + "') - file existed -> deleted!!");
            fconn = new File(filename);
        }
        fconn.createNewFile();
        DataOutputStream dos = null;
        FileOutputStream os = null;
        try {
            os = new FileOutputStream(fconn);
            dos = new DataOutputStream(os);
            obj.appendToDataOutput(dos);
        } finally {
            if (os != null) {
                os.close();
            }
            if (dos != null) {
                dos.close();
            }
        }
    }

    /**
     * Gives an {@link Serializable} object to be stored at the current Cache object with the given filename.
     * If an error occurs, the related method of the given {@link ErrorListener} object is called.
     */
	public void saveAsync(final String filename, final Serializable obj, final ErrorListener listener) {
        taskIOQueue.addTask(new Runnable() {

			public void run() {
                try {
                    save(filename, obj);
                } catch (Throwable ex) {
                    if (listener != null) {
                        listener.onError(new instar.dg.Common.Error(0, ex.toString()));
                    }
                    ex.printStackTrace();
                }
            }
        });
    }
}
